package com.lzl.redisratelimit.interceptor;

import com.lzl.redisratelimit.annotation.RateLimit;
import com.lzl.redisratelimit.annotation.RateLimitKey;
import com.lzl.redisratelimit.util.RateLimiter;
import com.sun.tools.javac.util.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Aspect
@Component
public class RateLimitInterceptor {

    @Resource
    private RateLimiter rateLimiter;

    @Around("execution(public * *(..)) && @annotation(com.lzl.redisratelimit.annotation.RateLimit)")
    public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        // 断言不会被限流
        assertNonLimit(rateLimit, pjp);
        return pjp.proceed();
    }

    private void assertNonLimit(RateLimit rateLimit, ProceedingJoinPoint pjp) {
        String rateLimitKey = getRateLimitKey(pjp);
        boolean limit = rateLimiter.isLimit(rateLimit.scene(), rateLimitKey, rateLimit.bucketCapacity(), rateLimit.addSize(), rateLimit.addRate());
        if (limit) {
            throw new RuntimeException("被限流了");
        }
    }

    private String getRateLimitKey(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        final Object[] args = pjp.getArgs();
        final Parameter[] parameters = method.getParameters();
        StringBuilder builder = new StringBuilder();
        //默认解析方法里面带 RateLimit 注解的属性,如果没有尝试着解析实体对象中的
        for (int i = 0; i < parameters.length; i++) {
            final RateLimitKey annotation = parameters[i].getAnnotation(RateLimitKey.class);
            if (annotation == null) {
                continue;
            }
            if (builder.length() == 0) {
                builder.append(args[i]);
            } else {
                builder.append(rateLimit.delimiter()).append(args[i]);
            }
        }
        if (StringUtils.isEmpty(builder.toString())) {
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                final Object object = args[i];
                final Field[] fields = object.getClass().getDeclaredFields();
                for (Field field : fields) {
                    final RateLimitKey annotation = field.getAnnotation(RateLimitKey.class);
                    if (annotation == null) {
                        continue;
                    }
                    field.setAccessible(true);
                    if (builder.length() == 0) {
                        builder.append(ReflectionUtils.getField(field, object));
                    } else {
                        builder.append(rateLimit.delimiter()).append(ReflectionUtils.getField(field, object));
                    }
                }
            }
        }
        return builder.toString();
    }

}
